/*
涂鸦工具
依赖于:html5 canvas,jQuery,font-awesome
需要Internet Explorer 9+, Firefox, Opera, Chrome 以及 Safari 
作者:徐东强 2017-07-20 xdq025@sina.com

2017-07-20 修复第二个涂鸦会不显示工具栏的错误,修复画布涂鸦位置偏移,实现change事件
2017-10-27 支持resize,注意，单独改变height、width不会自动缩放已经涂鸦的内容
2017-11-10 修复，文本工具不会记录历史记录，导致撤销会多撤销一步的BUG
2018-01-19 添加对固定大小的支持,防止不同分辨率的机器，加载同一图形时，绘画发生形变或失真
2018-09-05 BUG：当页面未设置行高时，文本工具会无效
*/



(function () {
    String.prototype.format = function (args) {
        /// <summary>格式化字符串</summary>
        var result = this;
        if (arguments.length > 0) {
            if (arguments.length == 1 && typeof (args) == "object") {
                for (var key in args) {
                    if (args[key] != undefined) {
                        var reg = new RegExp("({" + key + "})", "g");
                        result = result.replace(reg, args[key]);
                    }
                }
            }
            else {
                for (var i = 0; i < arguments.length; i++) {
                    if (arguments[i] != undefined) {
                        //var reg = new RegExp("({[" + i + "]})", "g");//这个在索引大于9时会有问题，谢谢何以笙箫的指出
                        var reg = new RegExp("({)" + i + "(})", "g");
                        result = result.replace(reg, arguments[i]);
                    }
                }
            }
        }
        return result;
    };

    String.format = function (text, args) {
        /// <summary>格式化字符串</summary>
        var myStr = Array.prototype.splice.call(arguments, 0, 1);
        return myStr.toString().format.apply(arguments);
    };

})();

(function ($) {
    var myCanvas = function (options) {
        /// <summary>我的画布</summary>
        var method = Array.prototype.shift.call(arguments)
            , options = Array.prototype.shift.call(arguments)
        var ret = null;
        this.each(function () {
            var $t = $(this), flagFourceRun = false;
            if (method === true) {
                flagFourceRun = true;
            }
            else if (typeof (method) == "object"
                || method === undefined
                || method == "render") {
                options = method;
                method = "render";
                var domData = $t.data();
                var opt = $.extend(true, {}, $.fn.myCanvas.defaults);
                opt = $.extend(opt, domData, options);
                $t.data("options", opt);
            }
            else if (method == "isLoaded") {
                ret = $t.data("options") != null;
                return;
            };
            var _options = $t.data("options");
            if (flagFourceRun) {
                //等待久了，强制执行
                _options = $.extend(true, {}, $.fn.myCanvas.defaults);
                method = "render";
                $t.data("options", _options);
                var tw = $t.data("width") || $t.width()
                    , pw = $t.parent().width()
                    , th = $t.data("height") || $t.height()
                    , ph = $t.parent().height()
                    , height = th
                    , width = tw;
                if (th == 0 || tw == 0) {
                    height = ph;
                    width = pw;
                };
                var height = Math.max(200, height);
                var width = Math.max(200, width);
                _options._width = width;
                _options._height = height;
            }
            else {
                if (!_options ||
               (!_options.hasInited && $t.is("img")
                    && ($t.height() == 0 || $t.width() == 0))) {
                    //用于延迟加载的情况
                    function onLoad() {
                        var $t = $(this);
                        var _options = $t.data("options");
                        if (!_options || !_options.$container) {
                            //1.请确认this是否是初始化myCanvas时的元素；
                            $t.myCanvas(true);
                            _options = $(this).data("options");
                        }
                        var caller = _options[method];
                        ret = caller.call(_options, options);
                        return;
                    }
                    $t.one("load", onLoad);
                    //2018-07-31 我不知道为什么要绑定Change事件，但是，会导致setData后图片变形
                    //.one("change", onLoad);
                    return;
                };
            }

            _options.$container = $t;
            var caller = _options[method];
            if (caller == null)
                throw "无法找到" + method;

            if ($.isFunction(caller))
                ret = caller.call(_options, options || _options);
            else
                ret = caller;
        });

        return (ret === undefined || ret === null) ? this : ret;
    };

    myCanvas.defaults = {
        data: {}
        , tools: ["pencil", "text", "colors", "width", "eraser", "undo", "redo", "clear"]
        , "render": function (opt) {
            var t = this;
            if (t.$wrapper) {
                t.destory();
            };
            opt._width = opt._width || t.$container.data("width") || t.$container.width();
            opt._height = opt._height || t.$container.data("height") || t.$container.height();
            var size = {
                "width": opt._width
                , "height": opt._height
            };
            if (size.width == 0 || size.height == 0)
                throw "无法获取初始化元素的尺寸，请确认图片资源是否正确，或是否可以提前设置大小";
            t.$container.css(size);
            t.$wrapper = $("<div/>")
                .addClass("canvas-container")
                .css(size);
            t.$canvas = $("<canvas/>")
                .addClass("my-canvas")
                .css(size)
                .attr(size);
            t.$tools = $("<div/>")
                .addClass("canvas-tools");

            t.$container.before(t.$wrapper);
            t.$wrapper.append(t.$container);
            t.$container
                .after(t.$tools)
                .after(t.$canvas);
            t.$canvas.data("model", t);
            for (var i = 0; i < t.tools.length; i++) {
                var tl = t.tools[i];
                var tool = Tools[tl];
                if (tool == null) {
                    tool = tl;
                    if (tool == null) {
                        console.log("尚未实现工具:" + tl);
                        continue;
                    }
                };
                var $tool = $("<span/>")
                .attr({
                    title: myCanvas.lang[tool.title]
                    , "data-tool": tl
                });
                $tool.addClass(tool.icon).addClass(tl);
                $tool.click(tool.exec);
                t.tools[i] = tool;
                tool.$ele = $tool;
                t.$tools.append($tool);
                if (tool.init)
                    tool.init($tool, t);
            }
            Tools.initScroll.call(t);
            if (t.tools.length)
                t.tools[0].$ele.click();
            if (t.$container.attr("readonly")) {
                t.readonly(true);
            }
            this._initMouse();
            t.hasInited = true;
            return t;
        }
        , "getData": function () {
            /// <summary>获取DataUrl</summary>
            return this.getCanvas().canvas.toDataURL("image/png");
        }
        , "setData": function (buffer) {
            var img = new Image();
            var t = this;
            img.onload = function () {
                //保存Size
                var cvs = t.getCanvas(), canvas = cvs.canvas;
                cvs.save();
                //var rateh = img.height / canvas.height
                //    , ratew = img.width / canvas.width;
                cvs.drawImage(img, 0, 0, canvas.width, canvas.height);
                Tools.saveHistroy(t, 1);
                //还原Size
                cvs.restore();
            }
            img.src = buffer;
            return this;
        }
        , "readonly": function (flag) {
            var t = this;
            if (flag === undefined) {
                return t._readonly;
            }
            t._readonly = !!flag;
            if (t._readonly) {
                t.$tools.hide();
                t._currentTool = null;
            }
            return t;
        }
        , "getCanvas": function () {
            /// <summary>获取画布</summary>
            //2017-08-22不再使用缓存
            return this.$canvas.get(0).getContext("2d");
        }
        , "dispose": function () {
            var t = this;
            t.$container.insertBefore(t.$wrapper);
            t.$wrapper.remove();
            delete t;
        }
        , "destory": function () {
            this.dispose.apply(this, arguments);
        }
        , "height": function (px) {
            var t = this;
            if (px) {
                px = parseInt(px);
                t.$canvas.height(px).attr("height", px);
                t.$container.height(px);
                t.$wrapper.height(px);
                return t;
            }
            return t.$container.height();
        }
        , "width": function (px) {
            var t = this;
            if (px) {
                px = parseInt(px);
                t.$canvas.width(px).attr("width", px);
                t.$container.width(px);
                t.$wrapper.width(px);
                return t;
            }
            return t.$container.height();
        }
        , "size": function (size) {
            var data = this.getData();
            this.height(size.height);
            this.width(size.width);
            this.setData(data);
        }
        , "reload": function () {
            /// <summary>重新加载图形。Chrome会在放大浏览器后，不显示之前被遮住的部分</summary>
            var dd = this.getData();
            this.$container.html("");
            var $html = this.$container.clone();
            $html.insertBefore(this.$wrapper);
            this.$wrapper.remove();
            delete this.$wrapper;
            delete this.$canvas;
            this.hasInited = false;
            $html.myCanvas(this);
            $html.myCanvas("setData", dd);
            delete this;
        }
        , $container: null//原元素
        , $wrapper: null//包裹，在最外层
        , $canvas: null//画布
        , $tools: null//工具
        , _readonly: false
        , _width: null
        , _height: null
        , _currentTool: null
        , _isMouseDownInCanvas: false
        , _currentColor: "#f00"//画笔颜色
        , _currentPenWidth: 3//画笔宽度
        , _currentPos: { left: 0, top: 0 }
        , _canvasHistory: []//绘画记录
        , _canvasHistoryPoint: -1//历史记录指针
        , _currentCanvasTextStyle: {}
        , _currentText: null//当前文本输入框
        , _currentPageScroll: { x: 0, y: 0 } //页面滚动量
        , __canvas: null
        , _pencilLoc: { x: 0, y: 0 }//画笔相对画布坐标
        , _textLoc: { x: 0, y: 0 }//文本相对画布坐标   
        , _mouseIn: function (e) {
            var t = $(this).find(".my-canvas").data("model");
            t.$relative.addClass("Z999");
        }
        , _mouseOut: function (e) {
            var t = $(this).find(".my-canvas").data("model");
            t.$relative.addclass("Z999");
        }
        , _initMouse: function () {
            //var t = this.$container.get(0);
            //while (t && t !== document && t !== window) {
            //    if ($(t).css("overflow") == "hidden") {
            //        this.$relative = $(t);
            //        break;
            //    };
            //    t = t.parentElement;
            //};
            //if (this.$relative == null)
            //    this.$relative = this.$wrapper.parent();
            //this.$relative.data("")
            //this.$wrapper
            //    .off("mouseover", this._mouseIn)
            //    .on("mouseover", this._mouseIn)
            //    .off("mouseout", this._mouseOut)
            //    .on("mouseout", this._mouseOut)
            //;
        }
    };

    myCanvas.lang = {
        Tool_Text: "文本工具/Text"
        , Tool_Pencil: "画笔工具/Pencil"
        , Tool_Eraser: "橡皮檫/Eraser"
        , Tool_Undo: "撤销/Undo"
        , Tool_Redo: "重做/Redo"
        , Tool_Clear: "清除/Clear"
        , Tool_Colors: "颜色/Color"
        , Tool_Width: "画笔粗细/Width"
        , Font_Bold: "加粗/Bold"
        , Font_Italic: "斜体/Italic"
        , Font_FangSong: "仿宋/Arial"
        , Font_Kaiti: "楷体/Kai Ti"
        , Font_YaHei: "微软雅黑/Ya Hei"
        , Tool_Del: "删除/Delete"
        , Tip_Del: "[#确认是否删除本张背景图片#]?"
        , Tool_Mouse: "鼠标/Mouse"
        , Tool_Pop: "[#Text.Enlarge#] "
    };

    var Tools = {
        active: function () {
            var $t = $(this);
            $expired = $t.parent().find(".actived");
            var model = $t.parentsUntil(".canvas-container").last().parent().find("canvas").data("model");
            $expired.each(function () {
                var $tool = $(this), tool = $tool.data("tool");
                tool = Tools[tool]
                if (tool != null && tool.cancel != null) {
                    tool.cancel(model);
                };
            });
            $expired.removeClass("actived");
            $t.addClass("actived");
        }
        , showEvent: function (e) {
            if (top.IsDebug) {
                var text = [];
                text.push("type:" + e.type);
                var pos = Tools.getLocation(null, e);
                text.push("x:" + pos.x);
                text.push("x:" + pos.y);
                parent.$("#statusBar").html(text.join(","));
            }
        }
        , _initScroll: function ($p, val) {
            val.x = 0; val.y = 0;
            var good = ["auto", "scroll"]
            while ($p.length && $p[0] != document) {
                var x = $p.css("overflow-x")
                    , y = $p.css("overflow-y");
                if (good.indexOf(x) >= 0 || good.indexOf(y) >= 0) {
                    if ($p.is("html")) {
                        val.x += $("body").scrollLeft();
                        val.y += $("body").scrollTop();
                    }
                    else {
                        val.x += $p.scrollLeft();
                        val.y += $p.scrollTop();
                    }
                }
                $p = $p.parent();
            };
        }
        , _OnScroll: function (e) {
            //由事件调用
            $(this).find(".my-canvas")
            .each(function () {
                var $t = $(this), model = $t.data("model");
                if (!model)
                    return;
                Tools._initScroll($t, model._currentPageScroll);
            })
        }
        , initScroll: function () {
            /// <summary>初始化页面滚动量</summary>
            //var t = this;
            //$(document).off("scroll", Tools._OnScroll)
            //.on("scroll", Tools._OnScroll);
            //Tools._initScroll(t.$canvas, t._currentPageScroll);
        }
        , getLocation: function (model, e) {
            /// <summary>获取鼠标在画布的坐标</summary>
            //bug:当页面出现滚动条，并滚动时，画笔会上移向下滚动量；向左移动向右滚动量
            if (e.offsetX == undefined) {
                var off = $(e.target).offset();
                if (e.originalEvent.targetTouches[0] == undefined) {
                    return { x: 0, y: 0 };
                };
                return {
                    x: e.originalEvent.targetTouches[0].pageX - off.left
                    , y: e.originalEvent.targetTouches[0].pageY - off.top
                }
            }
            return {
                x: e.offsetX
                , y: e.offsetY
            };
        }
        , saveHistroy: function (model, addon) {
            /// <summary>保存历史记录</summary>
            /// <param name="model" type="Object">画布对象</param>
            /// <param name="addon" type="int">历史记录步进，通常传入1</param>
            var cvs = model.getCanvas();
            var canvas = cvs.canvas;
            if (canvas.width == 0 || canvas.height == 0) {
                if (Tools.saveHistroy._Tick == undefined)
                    Tools.saveHistroy._Tick = 0;
                if (Tools.saveHistroy._Tick > 3000) {
                    return console.log("无法加载画布尺寸！");
                }
                return setTimeout(function () {
                    Tools.saveHistroy._Tick++;
                    Tools.saveHistroy(model, addon);
                }, 10);
            };
            var dd = cvs.getImageData(0, 0, canvas.width, canvas.height);
            if (addon === null) {
                addon = -model._canvasHistoryPoint;
            }
            model._canvasHistoryPoint = model._canvasHistoryPoint + addon;
            model._canvasHistory[model._canvasHistoryPoint] = dd;
            if (model._canvasHistoryPoint > 0) {
                model.$container
                    .data("value", model.$container.myCanvas("getData"))
                    .trigger("change");
            }
            else {
                model.$container.data("value", null);
            }
            model.$container.change();
        }
        , pencil: {
            title: "Tool_Pencil"
            , icon: "fa fa-pencil"
            , exec: function (e) {
                var $t = $(this),
                    model = $t.parentsUntil(".canvas-container")
                        .last().parent().find(".my-canvas")
                        .data("model");
                if (model._readonly) {
                    e.preventDefault();
                    return false;
                };
                Tools.active.call(this);
                model._currentTool = "pen";
                function onMouseDown(e) {
                    Tools.showEvent.apply(this, arguments);
                    var $t = $(this), model = $t.data("model");
                    if (model._currentTool == "pen") {
                        model._isMouseDownInCanvas = true;
                        var cvs = model.getCanvas();
                        model._pencilLoc = { x: cvs.canvas.offsetLeft, y: cvs.canvas.offsetTop };
                        var pos = Tools.getLocation(model, e, model._pencilLoc);
                        cvs.beginPath();
                        cvs.moveTo(pos.x, pos.y);
                        cvs.strokeStyle = model._currentColor;
                        cvs.shadowColor = model._currentColor;
                        cvs.lineWidth = model._currentPenWidth;

                        e.preventDefault();
                        return false;
                    }
                }
                function onMouseMove(e) {
                    Tools.showEvent.apply(this, arguments);
                    var $t = $(this), model = $t.data("model"), canvas = model.$canvas[0];
                    var cvs = model.getCanvas();
                    if (model._currentTool == "pen" && model._isMouseDownInCanvas) {
                        var loc = Tools.getLocation(model, e, model._pencilLoc);
                        cvs.lineTo(loc.x, loc.y);
                        cvs.stroke();
                        //console.log("pencil line {0}".format(JSON.stringify(loc)));
                    }
                }
                function onMouseUp() {
                    Tools.showEvent.apply(this, arguments);
                    var $t = $(this), model = $t.data("model");
                    if (model._currentTool == "pen" && model._isMouseDownInCanvas) {
                        var cvs = model.getCanvas();
                        cvs.closePath();
                        Tools.saveHistroy(model, 1);
                        //console.log("pencil up");
                    }
                    model._isMouseDownInCanvas = false;
                }
                model.$canvas
                    .unbind("mousedown")
                    .unbind("mouseup")
                    .unbind("mousemove")
                    .unbind("touchstart")
                    .unbind("touchend")
                    .unbind("touchmove")
                    .bind("mousedown", onMouseDown)
                    .bind("mouseup", onMouseUp)
                    .bind("mousemove", onMouseMove)
                    .bind("touchstart", onMouseDown)
                    .bind("touchend", onMouseUp)
                    .bind("touchmove", onMouseMove)
                ;

                e.preventDefault();
                return false;
            }
        }
        , text: {
            title: "Tool_Text"
            , icon: "fa fa-font"
            , exec: function (e) {
                var $t = $(this),
                    model = $t.parentsUntil(".canvas-container")
                        .last().parent().find(".my-canvas")
                        .data("model");
                if (model._readonly) {
                    e.preventDefault();
                    return false;
                };
                Tools.active.call(this);
                model._currentTool = "text";
                model.$canvas
                    .unbind("click")
                    .bind("click", Tools.text.click_Cancas);
                e.preventDefault();
                return false;
            }
            , cancel: function (model) {
                if (model._currentText) {
                    model._currentText.remove();
                    model._currentText = null;
                };
                if (model._textTool) {
                    model._textTool.remove();
                    delete model._textTool;
                };
                Tools.saveHistroy(model, 1);
            }

            , click_Cancas: function (e) {
                Tools.log("click_Cancas");
                var $t = $(this), model = $t.data("model");
                if (model._currentTool == "text") {
                    //当前已经是文本工具
                    if (e.fromElement == null || e.fromElement === e.toElement) {
                        //，并且，点击在Canvas上
                        var text = Tools.text.getInputedText(model);
                        if (text == null) {
                            Tools.text.showInputTool(e, $t, model);
                        }
                        else if (text.trim() == "") {
                            Tools.text.cancel(model);
                            Tools.text.showInputTool(e, $t, model);
                        }
                        else {
                            Tools.text.cancel(model);
                        }
                    }
                    else {
                        //点击在文本工具内
                    }
                }
                else {
                    //当前不是文本工具
                    if (model._currentText) {
                        Tools.text.cancel(model);
                    }
                }
            }
            , keyUp_Text: function (e) {
                var $t = $(this).parentsUntil(".canvas-container").last().parent().find("canvas")
                    , model = $t.data("model");
                return Tools.text.startWrite(e, $t, model, false);
            }
            , startWrite: function (e, $t, model) {
                var txt = Tools.text.getInputedText(model);
                var canvasElement = model.$canvas.get(0);
                var font = "{0} normal {1} {2} {3}"
                    .format(
                        model._currentCanvasTextStyle["font-style"]
, model._currentCanvasTextStyle["font-weight"]
, model._currentCanvasTextStyle["font-size"]
, model._currentCanvasTextStyle["font-family"]
                    );
                var height = Tools.text.getFontHeight(model);
                //清理掉上次写入的文本
                if (model._canvasHistoryPoint > 0) {
                    var img = model._canvasHistory[model._canvasHistoryPoint];
                    if (img)
                        model.getCanvas().putImageData(img, 0, 0);
                };
                Tools.text.write(canvasElement, height, txt, font, model._currentColor, model._textLoc.x, model._textLoc.y);
            }
            , write: function (cns, lh, text, font, color, left, top) {
                /// <summary>写字到Canvas上去</summary>
                /// <param name="cns" type="Element">Canvas元素</param>
                /// <param name="lh" type="int">字体行高</param>
                /// <param name="text" type="String">写入文本</param>
                /// <param name="font" type="String">字体信息</param>
                /// <param name="color" type="String">前景色</param>
                /// <param name="left" type="float">绘制的字离左边框的距离</param>
                /// <param name="top" type="float">绘制的字离顶部的距离</param>
                var ctx = cns.getContext("2d");
                var lineheight = lh;
                var text = text;

                ctx.width = cns.width;
                ctx.height = cns.height;

                //ctx.clearRect(0, 0, ctx.width, ctx.height);
                ctx.font = font;
                ctx.fillStyle = color;
                var lines = text.split('\n');
                for (var i = 0; i < lines.length; i++) {
                    var tl = lines[i];
                    ctx.fillText(tl, left, top + (i + 0.46) * lineheight);
                }
            }
            , getFontHeight: function (model) {
                /// <summary>获取字体行高</summary>
                var $ip = model.$container.parent().find(".canvas-text-input")
                    , lh = $ip.css("line-height")
                    , fs = $ip.css("font-size")
                    , lineHeight = parseFloat(lh)
                    , fontSize = parseFloat(fs);
                if (isNaN(lineHeight)) {
                    //未设置行高的情况
                    if (model.$span == null) {
                        model.$span = $("<span></span>");
                        model.$span.css({
                            "padding": "0"
                            , "margin": "0"
                            , "line-height": "normal"
                        });
                        model.$span.html("123456789 English //简体中文");
                        $("body").append(model.$span);
                    };
                    model.$span.show();
                    model.$span.css({
                        "font-size": fs
                        , "font-family": $ip.css("font-family")
                        , "font-style": $ip.css("font-style")
                        , "font-weight": $ip.css("font-weight")
                    });
                    lineHeight = model.$span.height();
                };
                //最小要有10px的高度
                lineHeight = Math.max(10, lineHeight);
                Tools.log(["lineHeight:", lineHeight].join(""));
                return lineHeight;
            }
            , getInputedText: function (model) {
                return model._currentText == null ? null : model._currentText.find(".canvas-text-input").val();
            }
            , showInputTool: function (e, $t, model) {
                top.model = model;
                if (model._canvasHistoryPoint <= 0) {
                    Tools.saveHistroy(model, 1);
                };
                var $text = $("<div/>")
                .addClass("canvas-text");
                model.$wrapper.append($text);
                model._currentText = $text;
                model._textLoc = Tools.getLocation(model, e, model._textLoc);
                console.log(JSON.stringify(model._textLoc));
                var template = '<div class="text-tool">\
<input type="checkbox" value="bold" id="font-weight"/>\
<label for="font-weight">{0}</label>\
<input type="checkbox" value="italic" id="font-style"/>\
<label for="font-style">{1}</label>\
<select id="font-size">\
<option>9px</option><option >12px</option><option selected=selected>15px</option><option>21px</option><option>30px</option><option>50px</option>\
</select>\
<select id="font-family">\
<option value="FangSong">{2}</option><option value="KaiTi">{3}</option><option value="Microsoft YaHei">{4}</option><option value="Arial">Arial</option>\
</select>\
</div>';
                template = template.format(myCanvas.lang.Font_Bold, myCanvas.lang.Font_Italic, myCanvas.lang.Font_FangSong, myCanvas.lang.Font_Kaiti, myCanvas.lang.Font_YaHei)
                var $tools = $(template).addClass("canvas-text-tools");
                //background-color:transparent;-webkit-text-fill-color:transparent;border:none;
                var $input = $("<textarea row=1 cols=20 ></textarea>")
                    //.addClass("form-control")
                .addClass("canvas-text-input");
                model.$tools.append($tools);
                model._textTool = $tools;
                model._textInput = $input;
                $text
                    .append($input)
                $text.css({
                    top: e.offsetY
                    , left: e.offsetX
                    , right: "0px"
                    //, "border-top": "1px dashed #eee"
                    //, "border-left": "1px dashed #eee"
                });
                $tools.find("input,select")
                    .change(function (e) {
                        var $t = $(this)
                            , id = $t.attr("id")
                            , val = null;
                        var model = $t.parentsUntil(".canvas-container")
                              .last().parent().find(".my-canvas").data("model")
                          , $input = model._textInput
                        ;
                        if ($t.is("input")) {
                            if ($t.prop("checked")) {
                                val = $t.val();
                            }
                            else {
                                val = "normal";
                            }
                        }
                        else {
                            val = $t.find(":selected");
                            if (val.attr("value")) {
                                val = val.attr("value");
                            }
                            else {
                                val = val.text().trim();
                            }
                        }
                        model._currentCanvasTextStyle[id] = val;
                        $input.css(id, val);
                        Tools.text.startWrite(e, $t, model);
                    }).change();
                $input
                    .css({
                        left: 0
                        , top: 0
                        , position: "absolute"
                        , "padding": 0
                        , width: "100%"
                    })
                    .on("keyup", Tools.text.keyUp_Text)
                    .focus();
                e.preventDefault();
                return false;
            }
        }
        , eraser: {
            title: "Tool_Eraser"
            , icon: "fa fa-eraser"
            , exec: function () {
                var $t = $(this),
                    model = $t.parentsUntil(".canvas-container")
                        .last().parent().find(".my-canvas")
                        .data("model");
                if (model._readonly) {
                    e.preventDefault();
                    return false;
                };
                Tools.active.call(this);
                model._currentTool = "eraser";
                function onMouseDown(e) {
                    var $t = $(this),
                     model = $t.data("model");
                    if (model._currentTool == "eraser") {
                        model._isMouseDownInCanvas = true;
                        var pos = Tools.getLocation(model, e, { x: 0, y: 0 });
                        var cvs = model.getCanvas();
                        var half = model._currentPenWidth / 2;
                        half = Math.ceil(half);
                        cvs.clearRect(pos.x - half, pos.y - half, 2 * half, 2 * half);
                        e.preventDefault();
                        return false;
                    }
                }

                function onMouseMove(e) {
                    var $t = $(this),
                     model = $t.data("model");
                    if (model._currentTool == "eraser" && model._isMouseDownInCanvas) {
                        var pos = Tools.getLocation(model, e, { x: 0, y: 0 });
                        var cvs = model.getCanvas();
                        var width = model._currentPenWidth * 4;//粗一点！
                        var half = width / 2;
                        cvs.clearRect(pos.x - half, pos.y - half, width, width);
                    }
                }


                function onMouseUp(e) {
                    var $t = $(this),
                     model = $t.data("model");
                    if (model._currentTool == "eraser" && model._isMouseDownInCanvas) {
                        Tools.saveHistroy(model, 1);
                    }
                    model._isMouseDownInCanvas = false;
                }

                model.$canvas
                    .unbind("mousedown")
                    .unbind("mouseup")
                    .unbind("mousemove")
                    .unbind("touchstart")
                    .unbind("touchend")
                    .unbind("touchmove")
                    .bind("mousedown", onMouseDown)
                    .bind("mouseup", onMouseUp)
                    .bind("mousemove", onMouseMove)
                    .bind("touchstart", onMouseDown)
                    .bind("touchend", onMouseUp)
                    .bind("touchmove", onMouseMove)
                ;
            }
        }
        , undo: {
            title: "Tool_Undo"
            , icon: "fa fa-undo"
            , exec: function (e) {
                var $t = $(this)
                    , model = $t.parentsUntil(".canvas-container")
                        .last().parent().find(".my-canvas")
                        .data("model");
                if (model._readonly) {
                    e.preventDefault();
                    return false;
                };
                var canvas = model.$canvas[0];
                var cvs = model.getCanvas();
                if (model._canvasHistoryPoint > 0)
                    model._canvasHistoryPoint--;
                var img = model._canvasHistory[model._canvasHistoryPoint];
                if (img)
                    cvs.putImageData(img, 0, 0);
                e.preventDefault();
                return false;
            }
            , init: function ($ele, model) {
                model._canvasHistoryPoint = -1;
                Tools.saveHistroy(model, 1);
            }
        }
        , redo: {
            title: "Tool_Redo"
            , icon: "fa fa-repeat"
            , exec: function (e) {
                var $t = $(this)
                    , model = $t.parentsUntil(".canvas-container")
                        .last().parent().find(".my-canvas")
                        .data("model");
                if (model._readonly) {
                    e.preventDefault();
                    return false;
                };
                var canvas = model.$canvas[0];
                var cvs = model.getCanvas();
                if (model._canvasHistoryPoint + 1 < model._canvasHistory.length)
                    model._canvasHistoryPoint++;
                var img = model._canvasHistory[model._canvasHistoryPoint];
                if (img)
                    cvs.putImageData(img, 0, 0);
                e.preventDefault();
                return false;
            }
        }
        , clear: {
            title: "Tool_Clear"
            , icon: "fa fa-remove"
            , exec: function (e) {
                var $t = $(this)
                    , model = $t.parentsUntil(".canvas-container")
                        .last().parent().find(".my-canvas")
                        .data("model");
                var canvas = model.$canvas[0];
                var cvs = model.getCanvas();
                cvs.clearRect(0, 0, canvas.width, canvas.height);
                var dd = cvs.getImageData(0, 0, cvs.canvas.width, cvs.canvas.height);
                Tools.saveHistroy(model, 1);
                e.preventDefault();
                return false;
            }
        }
        , colors: {
            title: "Tool_Colors"
            , icon: "fa fa-spinner"
            , options: ["#000", "#f00", "#0f0", "#00f", "#ff0", "#f0f", "#0ff", "#fff"]
            , exec: function (e) {
                var $t = $(this)
                , $menu = $t.data("menu")
                , colors = Tools.colors.options;
                if ($menu == null) {
                    $menu = $("<ul>")
                    .addClass("canvas-tools-menu")
                    .addClass("canvas-tools-colors")
                    for (var i = 0; i < colors.length; i++) {
                        var c = colors[i];
                        var $li = $("<li/>");
                        $li.addClass("fa fa-circle")
                        .css({
                            "color": c
                        })
                        .attr("data-color", c)
                        .data("control", this)
                        .click(function () {
                            var $t = $(this)
                            , model = $t.parentsUntil(".canvas-container")
                            .last().parent().find(".my-canvas")
                            .data("model");
                            model._currentColor = $t.data("color");
                            var ctr = $t.data("control")
                            $(ctr).css("color", model._currentColor);
                            $t.parent().hide();
                        });
                        $menu.append($li);
                    }
                    $t.parent().append($menu);
                }
                var pos = $t.position();
                Tools.active.call(this);
                $menu.css({
                    left: pos.left
                    , top: pos.top + $t.outerHeight(true)
                }).show();
                e.preventDefault();
                return false;
            },
            cancel:function(model){
                $(".canvas-tools-menu:visible").hide();
            }
            , init: function ($ele, model) {
                $ele.css("color", model._currentColor);
            }
        }
        , width: {
            title: "Tool_Width"
            , options: ["1px", "2px", "4px", "8px", "16px", "32px", "64px"]
            , icon: "fa fa-circle"
            , exec: function (e) {
                var $t = $(this)
               , $menu = $t.data("menu")
               , widths = Tools.width.options;
                if ($menu == null) {
                    $menu = $("<ul>")
                    .addClass("canvas-tools-menu")
                    .addClass("canvas-tools-width")
                    for (var i = 0; i < widths.length; i++) {
                        var w = widths[i], wi = parseInt(w);
                        var height = Math.max(21, wi).toString() + "px";
                        var $icon = $("<i>")
                            .addClass("fl")
                        .css({
                            width: w
                            , height: w
                            , border: "1px solid rgba(255, 255, 255,0)"
                            , "border-radius": w
                            , "background-color": "#000"
                            , "margin-top": wi > 12 ? "auto" : ((12 - wi).toString() + "px")
                        });
                        var $lable = $("<a/>")
                        .addClass("fr")
                        .html(w);
                        var $li = $("<li/>");
                        $li.append($icon).append($lable)
                        .css({
                            "line-height": height
                            , "height": height
                        })
                        $li
                        .click(function () {
                            var $t = $(this)
                            , model = $t.parentsUntil(".canvas-container")
                            .last().parent().find(".my-canvas")
                            .data("model");
                            model._currentPenWidth = parseInt($t.text());
                            $t.parent().hide();
                        });
                        $menu.append($li);
                    }
                    $t.parent().append($menu);
                }
                Tools.active.call(this);
                $menu.css({
                    left: 0
                    , top: $t.outerHeight(true)
                }).show();
                e.preventDefault();
                return false;
            }
            ,cancel:function(){
                $(".canvas-tools-menu:visible").hide();
            }
        }
        , del: {
            title: "Tool_Del"
            , icon: "fa fa-remove"
            , exec: function (e) {
                var $t = $(this)
                    , $container = $t.parentsUntil(".canvas-container")
                       .last().parent()
                    , $that = $container.find(".my-canvas")
                    , model = $that.data("model");
                if (model._readonly) {
                    e.preventDefault();
                    return false;
                };
                var rs = model.$container.triggerHandler("beforedelete");
                if (rs !== false) {
                    if (rs === "beforedelete") {
                        model.$container.on("confirmdelete", function () {
                            model.$container
                                .off("confirmdelete")
                            .off("canceldelete");
                            model.$container.trigger("delete");
                            $container.remove();
                        }).on("canceldelete", function () {
                            model.$container
                                .off("confirmdelete")
                            .off("canceldelete");
                        });
                    } else {
                        model.$container.trigger("delete");
                        $container.remove();
                        $(".pop-canvas").hide();
                    }
                };
                Tools.saveHistroy(model, 1);
                e.preventDefault();
                return false;
            }
        }
        , mouse: {
            title: "Tool_Mouse"
            , icon: "fa fa-mouse-pointer"
            , exec: function (e) {
                Tools.active.call(this);
                model._currentTool = "mouse";
            }
        }
        , pop: {
            title: "Tool_Pop"
            , icon: "fa fa-search-plus"
            , exec: function (e) {
                var $t = $(this)
                    , $container = $t.parentsUntil(".canvas-container")
                       .last().parent()
                    , $that = $container.find(".my-canvas")
                    , model = $that.data("model");

                var $icon = model.$wrapper.find(".pop");
                $icon.removeClass("fa-search-plus")
                .removeClass("fa-search-minus");

                if (model.$wrapper.parent().is(".pop-canvas")) {
                    model._parent.append(model.$wrapper);
                    $(".pop-canvas").hide();

                    $icon.addClass("fa-search-plus")
                }
                else {
                    model._parent = model.$wrapper.parent();
                    model._before = model.$wrapper.before();
                    model._after = model.$wrapper.after();
                    var $pop = $(".pop-canvas");
                    if ($pop.length == 0) {
                        $pop = $("<div></div>");
                        $pop.addClass("pop-canvas");
                        $pop.append($("<div></div>").addClass("cover"))
                        $("body").append($pop);
                    };
                    $pop.show();
                    $pop.append(model.$wrapper);

                    $icon.addClass("fa-search-minus")
                }

            }
        }
        , log: function (t) {
            top.console && top.console.log && top.console.log(t);
        }
    };

    myCanvas.Tools = Tools;


    $.fn.myCanvas = myCanvas;

    $(function () {
        $(document).on("click", ".pop-canvas .cover", function () {
            $(".pop-canvas .canvas-tools .pop").click();
        });
        $(document).on("dblclick", ".my-canvas", function () {
            $(this).parent().find(".canvas-tools .pop").click();
        });
        var $t = $(".myCanvas");
        $t.myCanvas();
    });

})(jQuery);
